package idgenerater

import (
	"errors"
	"github.com/bwmarrin/snowflake"
	"github.com/denisbrodbeck/machineid"
	"go.uber.org/zap"
	"hash/fnv"
	"sync/atomic"
	"time"
)

// 功能已经实现
// 优化点：
// 1. lastTimestamp 存储到数据库或者缓存中。停机重启后判断是否发生时钟回拨
// 2. 增加时钟回拨监控 例如Prometheus
// 3. 缓冲区大小，最大等待时间；可以动态调整。
// 4. 降级时钟回拨一直发生，切换生产id方式如 uuid->hash
// 5. 互斥锁改为 atomic 实现

const maxBuffer = 1000 // 缓冲区大小
const maxWait = 1000   // 最大等待时间

// IdGenerate id 生成器
type IdGenerate struct {
	logger        *zap.Logger     // 日志
	node          *snowflake.Node // 雪花算法
	lastTimestamp atomic.Int64    // 上次生成id时间戳
	//sync.Mutex                  // 锁 优化为 atomic
	buffer chan int64    // id 缓冲区
	close  chan struct{} // 关闭信号
}

func NewIdGenerate(logger *zap.Logger) (*IdGenerate, error) {
	mid, did, err := getMachineID()
	if err != nil {
		return nil, err
	}
	// 前5位为数据中心；后5位为机器id
	nodeId := (did << 5) | mid
	node, err := snowflake.NewNode(nodeId)
	if err != nil {
		return nil, err
	}

	id := &IdGenerate{
		logger: logger,
		node:   node,
		buffer: make(chan int64, maxBuffer),
		close:  make(chan struct{}),
	}

	go id.generate() // 异步预生成 id

	return id, nil
}

func (s *IdGenerate) generate() {
	// s.logger.Warn("run time", zap.Int64("startTime", time.Now().UnixNano()))
	for {
		select {
		case <-s.close:
			close(s.close)
			close(s.buffer)
			return
		default:
			if len(s.buffer) < maxBuffer {
				id, err := s.generateId()
				if err == nil {
					s.buffer <- id
				}
			} else {
				//s.logger.Warn("buffer full", zap.Int64("endTime", time.Now().UnixNano()))
				time.Sleep(10 * time.Millisecond)
			}
		}
	}
}

func (s *IdGenerate) generateId() (int64, error) {
	//s.Lock()
	//defer s.Unlock()

	lastTimestamp := s.lastTimestamp.Load()

	now := time.Now().UnixMilli()
	if now < lastTimestamp { // 发生时钟回拨;只要当前时间在上次生成的时间后面，生成的id就不会发生重复
		i := lastTimestamp - now
		if i > maxWait { // 时钟回拨时间超过了等待时间
			s.logger.Warn(">>> clock callback ", zap.Int64("wait", i))
			return 0, errors.New("clock callback exceeds the threshold")
		}

		time.Sleep(time.Duration(i) * time.Millisecond)
	}

	id := s.node.Generate().Int64()

	s.lastTimestamp.Store(now)

	//s.lastTimestamp = now
	// s.logger.Warn(">>> Generate", zap.Int64("id", id))
	return id, nil
}

func (s *IdGenerate) GetId() (int64, error) {
	select {
	case id := <-s.buffer:
		// s.logger.Info("buffer get")
		return id, nil
	default:
		// s.logger.Info("generate get")
		return s.generateId()
	}
}

func (s *IdGenerate) Close() {
	s.close <- struct{}{}
}

func getMachineID() (int64, int64, error) {
	id, _ := machineid.ID() // 获取机器唯一标识（如硬件哈希）
	hasher := fnv.New32a()
	_, err := hasher.Write([]byte(id))
	if err != nil {
		return 0, 0, err
	}
	hashSum := hasher.Sum32()
	workerId := int64(hashSum % 31) // 取模31
	datacenterId := int64((hashSum >> 5) % 31)
	return workerId, datacenterId, nil
}
